/*
 * @Author: song
 * @Description:二进制加密保存数据
 * @Date: 2024年04月14日 星期日 17:04:36
 * @Modify:
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;

namespace Function.Preferences
{
    public class BinaryPreferences : IPreferences
    {
        private static readonly byte[] DEFAULT_IV = Encoding.ASCII.GetBytes("3CsM3tcL8yJKiWlX");
        private static readonly byte[] DEFAULT_KEY = Encoding.ASCII.GetBytes("S63mndMynlTJxPn1");
#if UNITY_EDITOR
        private static readonly string DEFAULT_FILE = Directory.GetCurrentDirectory() + "/Data/prefs.dat";
#else
        private static readonly string DEFAULT_FILE = Application.persistentDataPath + "/prefs.dat";
#endif
        private string _file;
        private RijndaelManaged _cipher;
        private ICryptoTransform _enCryptor;
        private ICryptoTransform _deCryptor;
        private Dictionary<string, PrefObject> _entrys = new Dictionary<string, PrefObject>();


        public BinaryPreferences() : this(DEFAULT_FILE)
        {
        }

        public BinaryPreferences(string file)
        {
            if (file.IsNullOrEmpty())
                throw new ArgumentNullException();
            _file = file;
            _cipher = new RijndaelManaged() //加密
            {
                Mode = CipherMode.CBC,
                Padding = PaddingMode.PKCS7,
                KeySize = 128,
                BlockSize = 128,
                FeedbackSize = 128
            };
            _enCryptor = _cipher.CreateEncryptor(DEFAULT_KEY, DEFAULT_IV);
            _deCryptor = _cipher.CreateDecryptor(DEFAULT_KEY, DEFAULT_IV);
            Load();
        }

        private byte[] Encode(byte[] data)
        {
            return _enCryptor.TransformFinalBlock(data, 0, data.Length);
        }

        private byte[] Decode(byte[] data)
        {
            return _deCryptor.TransformFinalBlock(data, 0, data.Length);
        }

        private void Load()
        {
            if (!File.Exists(_file))
                return;
            _entrys.Clear();
            try
            {
                byte[] data = File.ReadAllBytes(_file);
                data = Decode(data);
                using (MemoryStream stream = new MemoryStream(data))
                {
                    using (BinaryReader reader = new BinaryReader(stream))
                    {
                        int count = reader.ReadInt32();
                        for (int i = 0; i < count; i++)
                        {
                            string key = reader.ReadString();
                            _entrys[key] = new PrefObject(reader);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogError(e);
            }
        }

        public void Save()
        {
            _file.DirectoryName().CreateDirectory();
            using (MemoryStream stream = new MemoryStream())
            {
                using (BinaryWriter writer = new BinaryWriter(stream))
                {
                    writer.Write(_entrys.Count);
                    foreach (var kv in _entrys)
                    {
                        writer.Write(kv.Key);
                        kv.Value.SavePrefObject(writer);
                    }

                    writer.Flush();
                }

                byte[] data = stream.ToArray();
                data = Encode(data);
                File.WriteAllBytes(_file, data);
            }
        }

        public int GetInt(string key, int defaultValue = 0)
        {
            if (_entrys.TryGetValue(key, out var prefObject))
            {
                return prefObject.IntValue;
            }

            return defaultValue;
        }

        public void SetInt(string key, int setValue)
        {
            if (_entrys.TryGetValue(key, out var prefObject))
            {
                prefObject.IntValue = setValue;
            }
            else
            {
                prefObject = new PrefObject(SPrefType.SPreInt)
                {
                    IntValue = setValue
                };
                _entrys.Add(key, prefObject);
            }
        }

        public float GetFloat(string key, float defaultValue = 0f)
        {
            if (_entrys.TryGetValue(key, out var prefObject))
            {
                return prefObject.FloatValue;
            }

            return defaultValue;
        }

        public void SetFloat(string key, float setValue)
        {
            if (_entrys.TryGetValue(key, out var prefObject))
            {
                prefObject.FloatValue = setValue;
            }
            else
            {
                prefObject = new PrefObject(SPrefType.SPreFloat)
                {
                    FloatValue = setValue
                };
                _entrys.Add(key, prefObject);
            }
        }

        public long GetLong(string key, long defaultValue = 0L)
        {
            if (_entrys.TryGetValue(key, out var prefObject))
            {
                return prefObject.LongValue;
            }

            return defaultValue;
        }

        public void SetLong(string key, long setValue)
        {
            if (_entrys.TryGetValue(key, out var prefObject))
            {
                prefObject.LongValue = setValue;
            }
            else
            {
                prefObject = new PrefObject(SPrefType.SPreLong)
                {
                    LongValue = setValue
                };
                _entrys.Add(key, prefObject);
            }
        }

        public double GetDouble(string key, double defaultValue = 0L)
        {
            if (_entrys.TryGetValue(key, out var prefObject))
            {
                return prefObject.DoubleValue;
            }

            return defaultValue;
        }

        public void SetDouble(string key, double setValue)
        {
            if (_entrys.TryGetValue(key, out var prefObject))
            {
                prefObject.DoubleValue = setValue;
            }
            else
            {
                prefObject = new PrefObject(SPrefType.SPreDouble)
                {
                    DoubleValue = setValue
                };
                _entrys.Add(key, prefObject);
            }
        }

        public string GetString(string key, string defaultValue = null)
        {
            if (_entrys.TryGetValue(key, out var prefObject))
            {
                return prefObject.StringValue;
            }

            return defaultValue;
        }

        public void SetString(string key, string setValue)
        {
            if (_entrys.TryGetValue(key, out var prefObject))
            {
                prefObject.StringValue = setValue;
            }
            else
            {
                prefObject = new PrefObject(SPrefType.SPreString)
                {
                    StringValue = setValue
                };
                _entrys.Add(key, prefObject);
            }
        }

        public void DeleteAll()
        {
            _entrys.Clear();
            Save();
        }

        public void DeleteKey(string key)
        {
            if (_entrys.ContainsKey(key))
                _entrys.Remove(key);
            Save();
        }
    }
}